package com.gomyb.common.page;

import com.gomyb.utils.RequestUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.domain.Sort;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author <a href="mailto:hellohesir@gmail.com">Mr_He</a>
 * 狂拽酷炫吊炸天的分页对象封装
 * <p>
 * 两种创建方法
 * 一：
 * map通过util:mapToBean转换到各自对应的参数中，字段条件会自动添加到data map中，然后调用findPage方法即可
 * 二：
 * new PageParam对象并设置他的属性值，将条件参数setter进data中,然后调用findPage方法
 * <p>
 * data遵循规范：
 * COLUMN           =   默认全词匹配
 * COLUMN_EQ        =
 * COLUMN_NO_EQ     !=
 * COLUMN_LIKE      like value
 */
@Setter
@Getter
public class PageParam {

    //页码1为第一页
    private Integer page = 0;

    //每页条数
    private Integer limit = 10;

    //开始处
    private Integer offset = 0;

    //排序字段
    private String sortName = "createTime";

    //排序方式
    private String sortOrder = "desc";

    //数据
    private Map<String, String> data = new HashMap<>();

    public Sort.Direction getSort() {
        return "desc".equals(sortOrder.toLowerCase()) ? Sort.Direction.DESC : Sort.Direction.ASC;
    }

    /**
     * 根据request对象 自动注入分页参数
     */
    public static PageParam getPageParam(HttpServletRequest request) {
        Map<String, String> param = RequestUtil.getParam(request);
        PageParam pageParam;
        if (param != null && param.size() > 0) {
            pageParam = Util.mapToBean(param);
        } else {
            pageParam = new PageParam();
        }
        Integer offset = pageParam.getLimit() * pageParam.getPage();
        pageParam.setOffset(offset);
        return pageParam;
    }

    /**
     * set页码时默认-1
     */
    public void setPage(Integer page) {
        if (page != null) {
            this.page = page - 1;
        }
    }

    public static class Util {

        /**
         * 父类的map参数名
         */
        private static final String FIELD_NAME = "data";

        public static PageParam mapToBean(Map<String, ?> param) {
            return mapToBean(param, true);
        }

        public static PageParam mapToBeanNoData(Map<String, ?> param) {
            return mapToBean(param, false);
        }

        /**
         * map 转 对象
         * 将多余属性放入对象中 field name 当中预留，根类型为map
         *
         * @param flag 是否映射其他对象
         * @return 指定class对象
         */
        public static PageParam mapToBean(Map<String, ?> param, boolean flag) {
            try {
                PageParam obj = PageParam.class.newInstance();

                Map<String, Object> otherParam = new HashMap<>(param.size() / 2);
                for (Map.Entry<String, ?> entry : param.entrySet()) {
                    Field field;
                    try {
                        field = obj.getClass().getDeclaredField(entry.getKey());
                    } catch (Exception e) {
                        if (e instanceof NoSuchFieldException) {
                            field = null;
                        } else {
                            throw new RuntimeException(e);
                        }
                    }
                    if (null != field) {
                        Method method = obj.getClass()
                                .getDeclaredMethod("set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1), field.getType());
                        Object value = field.getType().getConstructor(String.class).newInstance(entry.getValue());
                        method.invoke(obj, value);
                    } else if (flag) {
                        otherParam.put(entry.getKey(), entry.getValue());
                    }
                }
                if (flag && otherParam.size() > 0) {
                    Field data = obj.getClass().getDeclaredField(FIELD_NAME);
                    if (null != data) {
                        Method method = obj.getClass()
                                .getDeclaredMethod("set" + data.getName().substring(0, 1).toUpperCase() + data.getName().substring(1), data.getType());
                        method.invoke(obj, otherParam);
                    }
                }
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

    }

}